#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 共用体中的所有成员共用一段公共存储区
// 共用体变量所占内存就是其成员中所占内存空间最大的成员的内存大小
union un {
    int num; // 4字节
    char str[20]; // 20字节
    double b; // 8字节
    char c; // 1字节
};

// 大端存储与小端存储
void storage_mode() {
    union un u;
    u.num = 0x1234;
    printf("%x\n", u.num); // 1234
    printf("%x, %x\n", u.str[0], u.str[1]); // 34, 12 小端存储
}

void usage() {
    printf("sizeof union = %d\n", sizeof(union un)); // 24
    // sizeof(union un)为共用体中所占字节数最多的那个成员的大小
    // 虽然un应该为20字节，但编译器为提高访问速度而对数据结构进行了内存对齐
    // double类型通常要求8字节对齐，因此union un的大小需要是8的倍数

    union un u1, u2;
    u2 = u1; // 同类型共用体/结构体可以相互赋值
    printf("%p, %p, %p, %p, %p\n", &u1, &u1.num, &u1.str, &u1.b, &u1.c);
    // 共用体变量中所有成员的首地址相同，也是该共用体变量的地址

    u1.num = 1;
    u1.b = 0.1;
    strcpy(u1.str, "hello");
    u1.c = 'a';
    printf("%d, %s, %c, %c\n", u1.num, u1.str, u1.b, u1.c);
    // 由于每次赋值都在修改联合体的内存空间，最终的结果是内存中的值被多次覆盖
    // 导致打印时 u1.num 的值不可预测，因为它的值已经被后续赋值操作破坏了

    union un {
        int num; // 4字节
        char c; // 1字节
    } u = {.num = 1, .c = 'a'}; // 可以在定义时初始化
    // u.num = 1;
    // u.c = 'a';
    // u = 'A'; // 不能对共用体变量名赋值
    printf("%d, %c\n", u.num, u.c); // 97
    // 共用体变量中起作用的变量是最后一次存放的成员，存入一个新成员后，原有的成员就失去作用了
    // 共用体在同一时刻只能存放一个变量

    struct st {
        int num; // 4字节
        char c; // 1字节
    } s;
    s.c = 'A';
    // s = 'A'; // 也不能对结构体变量名赋值
    printf("%d\n", u); // 现行C语言标准可以直接引用共用体/结构体变量名
    printf("%d\n", s);

}

int main() {
    usage();

    // 共用体变量不能作为函数参数，但指向共用体变量的指针可以作为函数参数
    storage_mode();
    return 0;
}